1   /*
2    * Copyright (C) 2009 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.google.common.util.concurrent;
18  
19  import com.google.common.annotations.Beta;
20  import com.google.common.base.Supplier;
21  import com.google.common.base.Throwables;
22  
23  import java.util.concurrent.Executor;
24  import java.util.concurrent.TimeUnit;
25  import java.util.concurrent.TimeoutException;
26  
27  /**
28   * Base class for services that do not need a thread while "running"
29   * but may need one during startup and shutdown. Subclasses can
30   * implement {@link #startUp} and {@link #shutDown} methods, each
31   * which run in a executor which by default uses a separate thread
32   * for each method.
33   *
34   * @author Chris Nokleberg
35   * @since 1.0
36   */
37  @Beta
38  public abstract class AbstractIdleService implements Service {
39  
40    /* Thread names will look like {@code "MyService STARTING"}. */
41    private final Supplier<String> threadNameSupplier = new Supplier<String>() {
42      @Override public String get() {
43        return serviceName() + " " + state();
44      }
45    };
46  
47    /* use AbstractService for state management */
48    private final Service delegate = new AbstractService() {
49      @Override protected final void doStart() {
50        MoreExecutors.renamingDecorator(executor(), threadNameSupplier)
51            .execute(new Runnable() {
52              @Override public void run() {
53                try {
54                  startUp();
55                  notifyStarted();
56                } catch (Throwable t) {
57                  notifyFailed(t);
58                  throw Throwables.propagate(t);
59                }
60              }
61            });
62      }
63  
64      @Override protected final void doStop() {
65        MoreExecutors.renamingDecorator(executor(), threadNameSupplier)
66            .execute(new Runnable() {
67              @Override public void run() {
68                try {
69                  shutDown();
70                  notifyStopped();
71                } catch (Throwable t) {
72                  notifyFailed(t);
73                  throw Throwables.propagate(t);
74                }
75              }
76            });
77      }
78    };
79  
80    /** Constructor for use by subclasses. */
81    protected AbstractIdleService() {}
82  
83    /** Start the service. */
84    protected abstract void startUp() throws Exception;
85  
86    /** Stop the service. */
87    protected abstract void shutDown() throws Exception;
88  
89    /**
90     * Returns the {@link Executor} that will be used to run this service.
91     * Subclasses may override this method to use a custom {@link Executor}, which
92     * may configure its worker thread with a specific name, thread group or
93     * priority. The returned executor's {@link Executor#execute(Runnable)
94     * execute()} method is called when this service is started and stopped,
95     * and should return promptly.
96     */
97    protected Executor executor() {
98      return new Executor() {
99        @Override public void execute(Runnable command) {
100         MoreExecutors.newThread(threadNameSupplier.get(), command).start();
101       }
102     };
103   }
104 
105   @Override public String toString() {
106     return serviceName() + " [" + state() + "]";
107   }
108 
109   @Override public final boolean isRunning() {
110     return delegate.isRunning();
111   }
112 
113   @Override public final State state() {
114     return delegate.state();
115   }
116 
117   /**
118    * @since 13.0
119    */
120   @Override public final void addListener(Listener listener, Executor executor) {
121     delegate.addListener(listener, executor);
122   }
123   
124   /**
125    * @since 14.0
126    */
127   @Override public final Throwable failureCause() {
128     return delegate.failureCause();
129   }
130   
131   /**
132    * @since 15.0
133    */
134   @Override public final Service startAsync() {
135     delegate.startAsync();
136     return this;
137   }
138   
139   /**
140    * @since 15.0
141    */
142   @Override public final Service stopAsync() {
143     delegate.stopAsync();
144     return this;
145   }
146   
147   /**
148    * @since 15.0
149    */
150   @Override public final void awaitRunning() {
151     delegate.awaitRunning();
152   }
153   
154   /**
155    * @since 15.0
156    */
157   @Override public final void awaitRunning(long timeout, TimeUnit unit) throws TimeoutException {
158     delegate.awaitRunning(timeout, unit);
159   }
160   
161   /**
162    * @since 15.0
163    */
164   @Override public final void awaitTerminated() {
165     delegate.awaitTerminated();
166   }
167   
168   /**
169    * @since 15.0
170    */
171   @Override public final void awaitTerminated(long timeout, TimeUnit unit) throws TimeoutException {
172     delegate.awaitTerminated(timeout, unit);
173   }
174   
175   /**
176    * Returns the name of this service. {@link AbstractIdleService} may include the name in debugging
177    * output.
178    *
179    * @since 14.0
180    */
181   protected String serviceName() {
182     return getClass().getSimpleName();
183   }
184 }